package edu.predatorwars.client.visualization;

import java.awt.Point;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.Texture.TextureWrap;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.NinePatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.loaders.obj.ObjLoader;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.Ray;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actors.Button;
import com.badlogic.gdx.scenes.scene2d.actors.Image;
import com.badlogic.gdx.scenes.scene2d.actors.Button.ClickListener;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.Container;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Pane;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox.CheckBoxStyle;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox.CheckedListener;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Pane.PaneStyle;
import com.badlogic.gdx.utils.BufferUtils;

import edu.predatorwars.client.visualization.BoardRenderer.inputHandler;
import edu.predatorwars.shared.models.Buff;
import edu.predatorwars.shared.models.Creature;
import edu.predatorwars.shared.models.Resource;
import edu.predatorwars.shared.models.Terrain;


public class BoardRenderer implements ApplicationListener, BoardRendererInterface {
	//Models
	private Mesh world, mountain, gorilla, wolf, bear, elephant, snake, hawk, rabbit, ram, turtle, water;
	
	//GUI Stuff
	private Stage stage;
	
	private Container top_container;
	private Label lbl_myStats;
	private Label lbl_myBuffs;
	private Label lbl_turnsleft;
	private Button btn_endturn;
	private Button btn_exit;
	private CheckBox chk_autofollow;
	private Container message_container;
	private Label lbl_message;
	private Button btn_message;
	private Container mover_container;
	private Container select_container;
	private BitmapFont font_small;
	private BitmapFont font_large;
	private Label lbl_moverStats;
	private Label lbl_selectStats;
	private Label lbl_moverpre;
	private Label lbl_selectpre;
	private String prestats = "HP:\nStrength:  \nArmor:\nSight:\nSpeed:\n";

	
	//mover/selected player for gui
	private VisModel selectedModel;
	private VisModel moverModel;
	
	//Shaders
	private ShaderProgram worldShader, hexEmptyShader, hexWaterShader, hexTextureShader, toonShader, waterShader;
	//private Matrix4 worldMatrix, hexMatrix; 
	
	//Textures
	private IntBuffer mTex;
	private Pixmap[] worldFaces;
	private Texture noise, grass, toon;
	
	//GUI Textures
	private Texture blankAlpha, blank, alpha, exitBtn, exitinvBtn, endturnBtn, endturninvBtn, closeBtn, closeinvBtn, checked, unchecked;
	
	
	
	
	//Camera
	private Camera cam;
	
	//Input (used for mouse scroll event)
	private inputHandler input;
	

	
	//Shader settings
	private float	time 		= 0f,
					wFadeExp 	= 6.08f,
					wFadeBias	= 0.30f,
					wWaveSpeed 	= 0.34f,
					wNoiseSpeed	= 0.18f;
	
	private float[] light0 = {-100,0,0,1};
	private float[] light1 = {100,-100,0,1};
	private float[] light2 = {0,0,100,1};
	
	private float[] wScale = {0.5f, 1.0f, 0.5f};
	private float[] wColor = {0.1960784f,0.345098f,0.6823529f,1.0f};
	private float[] black = {0f,0f,0f,0f};
	private float[] green = {0f,0.5f,0f,1f};
	private float[] red = {0.5f, 0f,0f,1f};
	
	//State controlling variables -- client changes these fields
	
	private List<VisPlayer> players;
	private List<VisResource> resources; //all resources except water (water is rendered with a different shader)
	private List<VisResource> waterResources;
	
	private List<VisModel> moverModels;
	
	private VisPlayer myPlayer;
	
	//Lists for grouping models (faster rendering time)
	private List<VisTerrain> waterTerrain;
	private List<VisTerrain> grassTerrain;
	private List<VisTerrain> emptyTerrain;
	private List<DataSet<VisTerrain, VisModel>> mountainTerrain;
	
	//lookup board (used for mouse over / click events)
	private VisLocation[][] visBoard;
	
	//valid moves
	private List<Point> moves;
	
	//more state variables
	private Boolean sLoading;
	private Boolean dialogOpen = false;
	
	private Boolean autofollow = false;
	private Vector2 targetDistance;
	
	private VisualizationStarter visualizationStarter;
	
	
	public BoardRenderer(VisualizationStarter vs) {

		//hexGrid = new VisTerrain[0][0];
		sLoading = false;
	
		mTex = BufferUtils.newIntBuffer(2);
		input = new inputHandler();
		visualizationStarter = vs;
		
		players = new ArrayList<VisPlayer>();
		resources = new ArrayList<VisResource>();
		waterResources = new ArrayList<VisResource>();
		moves = new ArrayList<Point>();
		
		moverModels = new ArrayList<VisModel>();
		
		waterTerrain = new ArrayList<VisTerrain>();
		grassTerrain = new ArrayList<VisTerrain>();
		emptyTerrain = new ArrayList<VisTerrain>();
		mountainTerrain = new ArrayList<DataSet<VisTerrain, VisModel>>();
		
		targetDistance = new Vector2(0.001f,-1);
		
		visBoard = new VisLocation[0][0];
	}
	
	public void showLoading(Boolean b) {
		sLoading = b;
	}

	
	public void showWin(Boolean b) {
		message_container.visible = b;
		if (b) {
			dialogOpen = true;
			lbl_message.setText("You Win! =)");
			lbl_message.layout();
			message_container.x = Gdx.graphics.getWidth() / 2 - 70;
	
		}
		else dialogOpen = false;
	}
	
	public void showLose(Boolean b) {
		message_container.visible = b;
		if (b) 
		{
		lbl_message.setText("You Lose! =(");
		lbl_message.layout();
		message_container.x = Gdx.graphics.getWidth() / 2 - 75;
		dialogOpen = true;
		}
		else dialogOpen = false;
	}
	
	public void showDisconnected(Boolean b) {
		message_container.visible = b;
		if (b)
		{
		lbl_message.setText("Connection to the server has been lost.");
		lbl_message.layout();
		message_container.x = Gdx.graphics.getWidth() / 2 - 200;
		dialogOpen = true;
		}
		else dialogOpen = false;
	}
	
	public void showMyTurn(Boolean b) {
		message_container.visible = b;
		if (b) {
			lbl_message.setText("It's feeding time!\n");
			lbl_message.layout();
			message_container.x = Gdx.graphics.getWidth() / 2 - 100;
			dialogOpen = true;
		}
		else dialogOpen = false;
	}
	
	public void setMoves(List<Point> Moves) {
		moves = Moves;
	}
	
	public void setBoard(VisLocation[][] currentBoard) {
		visBoard = currentBoard;
	}
	
	public void setMyPlayer(VisPlayer player) {
		myPlayer = player;
		autofollow = true;
	}
	
	
	
	public void setTurnsLeft(String turns) {
		lbl_turnsleft.setText(turns);
	}
	
	public void updatePlayers(List<TDataSet<VisPlayer, PlayerExt, Point>> Players) {
		for (int i=0;i<Players.size();i++) {
			TDataSet<VisPlayer, PlayerExt, Point> dsp = Players.get(i);
			VisPlayer vplayer = dsp.getKey();
			PlayerExt player = dsp.getValue1();
			Point p = player.getPlayerLocation();
			Point len = dsp.getValue2();
			
			if (!p.equals(vplayer.getPlayer().getPlayerLocation()))
				vplayer.move(p.x, p.y, len.x, len.y);
			
			vplayer.setPlayer(player);
		}
	}
	
	public void addPlayers(List<VisPlayer> Players) {
		for (int i=0;i<Players.size();i++) {
			VisPlayer vplayer = Players.get(i);
			players.add(vplayer);
			moverModels.add(vplayer);
			
			switch(vplayer.getPlayer().getCreature().getCreatureType()) {
			case Creature.TYPE_BEAR :		vplayer.setModel(bear);
											break;
			case Creature.TYPE_ELEPHANT :	vplayer.setModel(elephant);
											break;
			case Creature.TYPE_GORILLA :	vplayer.setModel(gorilla);
											break;
			case Creature.TYPE_SNAKE :		vplayer.setModel(snake);
											break;
			case Creature.TYPE_WOLF :		vplayer.setModel(wolf);
											break;
			}
			
		}
	}
	
	public void removePlayers(List<VisPlayer> Players) {
		for (int i=0;i<Players.size();i++) {
			VisPlayer vplayer = Players.get(i);
			players.remove(vplayer);
			moverModels.remove(vplayer);
			if (vplayer.equals(moverModel)) moverModel = null;
			if (vplayer.equals(selectedModel)) selectedModel = null;
		}
	}
	
	public void addResources(List<VisResource> Resources) {
		for (int i=0;i<Resources.size();i++) {
			VisResource vr = Resources.get(i);
			moverModels.add(vr);
			int type = vr.getResource().getResourceType();
			
			switch(type) {
			case Resource.HAWK		:	resources.add(vr);
										vr.setModel(hawk);
										break;
			case Resource.RAM		:	resources.add(vr);
										vr.setModel(ram);
										break;
			case Resource.RABBIT	:	resources.add(vr);
										vr.setModel(rabbit);
										break;
			case Resource.TURTLE	:	resources.add(vr);
										vr.setModel(turtle);
										break;
			case Resource.WATER		:	waterResources.add(vr);	
										vr.setModel(water);
										break;
			}
		}
	}

	public void removeResources(List<VisResource> Resources) {
		for (int i=0;i<Resources.size();i++) {
			VisResource vr = Resources.get(i);
			moverModels.remove(vr);
			if (vr.equals(selectedModel)) selectedModel = null;
			if (vr.equals(moverModel)) moverModel = null;
			switch (vr.getResource().getResourceType()) {
			case Resource.WATER:	waterResources.remove(vr);
									break;
			default:				resources.remove(vr);
									break;
			}
		}
	}
	
	//TODO: don't think this function is necessary unless the same type of resource can have a different description...only implement if necessary
	public void updateResources() {
		//update descriptions
	}
	
	//three functions to update terrain changes---this is the best implementation so far...no visible lag when changing terrain quickly
	public void swapTerrain(List<DataSet<VisTerrain, TerrainExt>> swap) {
		for (int i=0;i<swap.size();i++) {
			VisTerrain current = swap.get(i).getKey();
			TerrainExt newTerrain = swap.get(i).getValue();
			TerrainExt oldTerrain = current.getTerrain();
			current.setTerrain(newTerrain);
			
			switch(newTerrain.getTerrainType()) {
			case Terrain.EMPTY: emptyTerrain.add(current); break;
			case Terrain.WATER: waterTerrain.add(current); break;
			case Terrain.GRASS: grassTerrain.add(current); break;
			case Terrain.MOUNTAIN: Point p = current.getTerrain().getTerrainLocation();
								   mountainTerrain.add(new DataSet<VisTerrain,VisModel>(current, new VisModel(p,visBoard.length, visBoard[p.x].length,false )));
								   break;
			}
			
			switch(oldTerrain.getTerrainType()) {
			case Terrain.EMPTY: emptyTerrain.remove(current); break;
			case Terrain.WATER: waterTerrain.remove(current); break;
			case Terrain.GRASS: grassTerrain.remove(current); break;
			case Terrain.MOUNTAIN: mountainTerrain.remove(new DataSet<VisTerrain, VisModel> (current,null)); ;break;
			}
		}
	}
	
	public void addTerrain(List<DataSet<VisTerrain, FPoint>> add) {
		for (int i=0;i<add.size();i++) {
			VisTerrain current = add.get(i).getKey();
			FPoint point = add.get(i).getValue();
			mkHexMesh(current,point.getX(),point.getY());
	
			switch (current.getTerrain().getTerrainType()) {
			case Terrain.EMPTY :	emptyTerrain.add(current); break;
			case Terrain.WATER :	waterTerrain.add(current); break;
			case Terrain.GRASS :	grassTerrain.add(current); break;
			case Terrain.MOUNTAIN : Point p = current.getTerrain().getTerrainLocation();
									mountainTerrain.add(new DataSet<VisTerrain, VisModel>(current, new VisModel(p, visBoard.length, visBoard[p.x].length, false))); break;
			}

		}

	}
	
	public void disposeTerrain(List<VisTerrain> dispose) {
		for (int i=0;i<dispose.size();i++) {
			VisTerrain current = dispose.get(i);
			switch (current.getTerrain().getTerrainType()) {
			case Terrain.EMPTY :	emptyTerrain.remove(current); break;
			case Terrain.WATER :	waterTerrain.remove(current); break;
			case Terrain.GRASS :	grassTerrain.remove(current); break;
			case Terrain.MOUNTAIN :	mountainTerrain.remove(current); break;
			}
			current.getModel().dispose();

		}
	}
	
	
	

	private void mkHexMesh(VisTerrain visTerrain, float x, float y) {
		Mesh hexSq;
		hexSq = new Mesh(true, 6, 12, new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.Generic, 3, "a_offset"));

		
		float[] Vertices = {
			 0.0f+x,	0.2f+y,		0f,
			-0.2f+x,	0.1f+y,		0f,
			 0.2f+x,	0.1f+y,		0f,
			-0.2f+x,   -0.1f+y,		0f,
			 0.2f+x,   -0.1f+y,		0f,
			 0.0f+x,   -0.2f+y,		0f
		};
		short[] Indices = {
				0, 1, 2,
				1, 2, 3,
				2, 3, 4,
				3, 4, 5,
		};
		
		hexSq.setVertices(new float[] {
			//vertices										//normals		offset
			Vertices[0],Vertices[1],Vertices[2],			0f, 0f, 1f,		x, y, 0,
			Vertices[3],Vertices[4],Vertices[5],			0f, 0f, 1f,		x, y, 0,
			Vertices[6],Vertices[7],Vertices[8],			0f, 0f, 1f,		x, y, 0,
			Vertices[9],Vertices[10],Vertices[11],			0f, 0f, 1f,		x, y, 0,
			Vertices[12],Vertices[13],Vertices[14],			0f, 0f, 1f,		x, y, 0,
			Vertices[15],Vertices[16],Vertices[17],			0f, 0f, 1f,		x, y, 0
		});
		hexSq.setIndices(new short[] {
			Indices[0],Indices[1],Indices[2],
			Indices[3],Indices[4],Indices[5],
			Indices[6],Indices[7],Indices[8],
			Indices[9],Indices[10],Indices[11]
		});
		
		visTerrain.setModel(hexSq);
		visTerrain.setIndices(Indices);
		visTerrain.setVertices(Vertices);
	}
	
	private void mkModels() {
		
		
		
		world = new Mesh(true, 8, 36, new VertexAttribute(Usage.Position, 3, "a_position"));
		world.setVertices(new float[] {
			-1f, -1f,  1f,
			-1f,  1f,  1f,
			 1f,  1f,  1f,
			 1f, -1f,  1f,
			-1f, -1f, -1f,
			-1f,  1f, -1f,
			 1f,  1f, -1f,
			 1f, -1f, -1f
		});
		world.setIndices(new short[] {
			//front
			0, 1, 2,
			0, 2, 3,
			//backf
			4, 5, 6,
			4, 6, 7,
			//left side
			0, 1, 4,
			4, 5, 1,
			//right side
			2, 3, 7,
			6, 7, 2,
			//top
			1, 2, 6,
			6, 5, 1,
			//bottom
			0, 3, 7,
			7, 4, 0
			
		});
		
		mountain = new Mesh(true, 7, 18, new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"));
		mountain.setVertices(new float[] {
				//vertices										//normals		
				 0.0f,	0.2f,	0.0f,							 0f,  1f, 0f,
				-0.2f,	0.1f,	0.0f,							-1f,  1f, 0f,		
				 0.2f,	0.1f,	0.0f,							 1f,  1f, 0f,		
				-0.2f, -0.1f,	0.0f,							-1f, -1f, 0f,		
				 0.2f, -0.1f,	0.0f,							 1f, -1f, 0f,	
				 0.0f, -0.2f,	0.0f,				 			 0f, -1f, 0f,	
				 0.0f,	0.0f,	0.4f,							 0f,  0f, 1f
		});
		mountain.setIndices(new short[] {
				0, 1, 6,
				2, 0, 6,
				4, 2, 6,
				5, 4, 6,
				3, 5, 6,
				1, 3, 6
		});
		
		//mountain = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/mountain.obj").read());
		//mountain.scale(0.15f,0.15f,0.15f);
		//player models
		gorilla = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/gorilla.obj").read());
		wolf = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/wolf.obj").read());
		bear = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/bear.obj").read());
		elephant = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/elephant.obj").read());
		snake = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/snake.obj").read());
		hawk = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/hawk.obj").read());
		ram = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/ram.obj").read());
		turtle = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/turtle.obj").read());
		rabbit = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/rabbit.obj").read());
		water = ObjLoader.loadObj(Gdx.files.internal("visualization/trunk/resources/client/visualization/models/water.obj").read());
	}
	
	private void mkSprites() {
		
		
		font_small = new BitmapFont();
		font_small.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.MipMapLinearLinear);
		font_small.setUseIntegerPositions(true);
		
		font_large = new BitmapFont(Gdx.files.internal("visualization/trunk/resources/client/visualization/fonts/verdana39.fnt"),Gdx.files.internal("visualization/trunk/resources/client/visualization/fonts/verdana39.png"),false);
		font_large.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.MipMapLinearLinear);
		font_large.scale(-0.4f);
		font_large.setUseIntegerPositions(true);
		
		Pixmap pix1 = new Pixmap(1,1,Format.RGBA4444);
		Pixmap pix2 = new Pixmap(1,1,Format.RGB888);
		
		blankAlpha = new Texture(pix1);
		blank = new Texture(pix2);
		alpha =  new Texture(20,30,Format.Alpha);
		
		exitBtn = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/exit_button.png"));
		exitinvBtn = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/exit_button_inv.png"));
		endturnBtn = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/endturn_button.png"));
		endturninvBtn = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/endturn_button_inv.png"));
		closeBtn = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/close_button.png"));
		closeinvBtn = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/close_button_inv.png"));
		checked = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/checkmark.png"));
		unchecked = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/checkmark_un.png"));
		
		LabelStyle lwhite = new LabelStyle(font_small, Color.WHITE);
		LabelStyle lgreen = new LabelStyle(font_small, Color.GREEN);
		LabelStyle lgray = new LabelStyle(font_small, new Color(0,0.5f,0.5f,1));
		LabelStyle lblack_large = new LabelStyle(font_large, Color.BLACK);
	
		stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);

		
		//top GUI bar
		top_container = new Container("top",0, 20);
		Pane top_pane = new Pane("top_pane", stage, 0,0,new PaneStyle(new NinePatch(blankAlpha,0,250,0,10)));
		
		
		
		Label text0 = new Label("title",prestats, lwhite);
		lbl_myStats = new Label("stats","", lwhite);
		lbl_myBuffs = new Label("buffs","", lgreen);
		lbl_turnsleft = new Label("turns","Moves Left:   0", lgray);
		
		btn_endturn = new Button("end_turn", new TextureRegion(endturnBtn), new TextureRegion(endturninvBtn));
		btn_exit = new Button("exit", new TextureRegion(exitBtn), new TextureRegion(exitinvBtn));
		chk_autofollow = new CheckBox("autofollow","Autofollow", new CheckBoxStyle(font_small, Color.WHITE, new TextureRegion(checked), new TextureRegion(unchecked)));
		
		btn_endturn.clickListener = new ClickListener() {
			@Override
			public void clicked(Button button) {
				visualizationStarter.endedTurn();
			}
		};
		btn_exit.clickListener = new ClickListener() {
            @Override
            public void clicked(Button button) {
                visualizationStarter.exitGame();
            }
        };
		chk_autofollow.setCheckedListener(new CheckedListener() {
			@Override
			public void checked(CheckBox checkBox, boolean isChecked) {
				if (myPlayer != null) {
				autofollow = isChecked;
				setTargetDistance();
				
				}
			}

		});
		
	
		
		top_container.y=70;//Gdx.graphics.getHeight();


		top_pane.add(text0);
		top_pane.add(lbl_myStats);
		top_pane.add(lbl_myBuffs);
		
		top_container.add(top_pane);

		
		chk_autofollow.y = -8+ (btn_exit.y = -60);
		btn_exit.x = Gdx.graphics.getWidth() - top_container.x-75;
		
		chk_autofollow.x = btn_exit.x -150;

	
		stage.addActor(top_container);
	
		btn_endturn.y= 5;
		btn_endturn.x = 300;
		lbl_turnsleft.x = btn_endturn.x+25;
		
		lbl_turnsleft.y=50;
		stage.addActor(btn_endturn);
		stage.addActor(lbl_turnsleft);
		top_container.addActor(btn_exit);
		top_container.addActor(chk_autofollow);
		
		//mouseover/click creature stats
		Pane p1 = new Pane("pane", stage, 0, 0, new PaneStyle(new NinePatch(blankAlpha,0,1,0,1)));
		Pane p2 = new Pane("pane", stage, 0, 0, new PaneStyle(new NinePatch(blankAlpha,0,1,0,1)));
		mover_container = new Container("mover_container", 0, 0);
		select_container = new Container("select_container",0,0);
		
		lbl_moverpre = new Label("title","", lwhite);
		lbl_selectpre = new Label("title1","", lwhite);
		lbl_moverStats = new Label("stats","", lwhite);
		lbl_selectStats = new Label("stats1","",lwhite);

		p1.add(lbl_moverpre);
		p2.add(lbl_selectpre);		
		p1.add(lbl_moverStats);
		p2.add(lbl_selectStats);
		mover_container.add(p1);
		select_container.add(p2);

		mover_container.visible = false;
		select_container.visible = false;
		
		stage.addActor(mover_container);
		stage.addActor(select_container);

		//message container
		
		message_container = new Container("message",0,0);
		Pane message_pane = new Pane("message_pane", stage,0,0, new PaneStyle(new NinePatch(new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/blank_pane.png"),true),0,0,0,0)));
		lbl_message = new Label("lbl_message","",lblack_large);
		btn_message = new Button("btn_message",new TextureRegion(closeBtn), new TextureRegion(closeinvBtn));
		
		message_pane.add(lbl_message);
		message_pane.row();
		message_pane.add( new Image("space",alpha));
		message_pane.row();
		message_pane.add(btn_message);
		message_pane.padding(20, 20, 15, 20);
	
		message_container.add(message_pane);
		
		message_container.y = Gdx.graphics.getHeight() / 2;
		message_container.x = Gdx.graphics.getWidth() / 2;
		message_container.visible = false;
		
		btn_message.clickListener = new ClickListener() {
			@Override
			public void clicked(Button button) {
				visualizationStarter.confirmedModalDialog();
			}
		};
		
		stage.addActor(message_container);
	}


	
	private void mkShaders() {
		worldShader = new ShaderProgram(Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/WorldShader-Vert.glsl").readString(), Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/WorldShader-Frag.glsl").readString());
		hexEmptyShader = new ShaderProgram(Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/HexEmptyShader-Vert.glsl").readString(), Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/HexEmptyShader-Frag.glsl").readString());
		hexWaterShader = new ShaderProgram(Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/HexWaterShader-Vert.glsl").readString(), Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/HexWaterShader-Frag.glsl").readString());
		hexTextureShader = new ShaderProgram(Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/HexTextureShader-Vert.glsl").readString(), Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/HexTextureShader-Frag.glsl").readString());
		toonShader = new ShaderProgram(Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/ModelToonShader-Vert.glsl").readString(), Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/ModelToonShader-Frag.glsl").readString());
		waterShader = new ShaderProgram(Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/WaterShader-Vert.glsl").readString(), Gdx.files.internal("visualization/trunk/resources/client/visualization/shaders/WaterShader-Frag.glsl").readString());
	}
	
	
	private void mkTextures() {
		
		
		
		//create world texture (cubemap
		worldFaces = new Pixmap[6];
		worldFaces[0] = new Pixmap(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/g2.jpg"));
		worldFaces[1] = new Pixmap(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/g4.jpg"));
		worldFaces[2] = new Pixmap(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/g5.jpg"));
		worldFaces[3] = new Pixmap(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/g6.jpg"));
		worldFaces[4] = new Pixmap(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/g1.jpg"));
		worldFaces[5] = new Pixmap(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/g3.jpg"));

		GL20 gl  = Gdx.gl20;
		gl.glGenTextures(2, mTex);

		gl.glBindTexture(GL20.GL_TEXTURE_CUBE_MAP, mTex.get(0));
		for (int i=0;i<6;i++) {
			gl.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, worldFaces[i].getGLInternalFormat(), worldFaces[i].getWidth(), worldFaces[i].getHeight(), 0, worldFaces[i].getGLFormat(), worldFaces[i].getGLType(), worldFaces[i].getPixels());
		}
		
		gl.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR);
		gl.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR);

		gl.glBindTexture(GL20.GL_TEXTURE_CUBE_MAP, mTex.get(1));
		for (int i=0;i<6;i++) {
			gl.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, worldFaces[3].getGLInternalFormat(), worldFaces[3].getWidth(), worldFaces[3].getHeight(), 0, worldFaces[3].getGLFormat(), worldFaces[3].getGLType(), worldFaces[3].getPixels());
		}
		gl.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR);
		gl.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR);
		
		noise = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/noise.png"), true);
	
		noise.setFilter(TextureFilter.MipMapLinearLinear, TextureFilter.Linear);
		noise.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
		
		grass = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/grass.jpg"),true);

		grass.setFilter(TextureFilter.MipMapLinearLinear, TextureFilter.Linear);
		grass.setWrap(TextureWrap.ClampToEdge, TextureWrap.ClampToEdge);
		
		toon = new Texture(Gdx.files.internal("visualization/trunk/resources/client/visualization/textures/toon_outline.jpg"), Format.RGBA8888, true);
		
		toon.setFilter(TextureFilter.MipMapLinearLinear, TextureFilter.Linear);
		toon.setWrap(TextureWrap.ClampToEdge, TextureWrap.ClampToEdge);
		
	}

	
	Matrix4 rot;
	
	@Override
	public void create() {
		mkModels();
		mkShaders();
		
		mkTextures();
		mkSprites();
		
		float maxWidth =  ((Gdx.graphics.getWidth() > Gdx.graphics.getHeight()) ? Gdx.graphics.getWidth() : Gdx.graphics.getHeight());
		
		cam = new PerspectiveCamera(67f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		cam.far = maxWidth * 2f;
		cam.position.z += 3.5;
		cam.position.y -= 0.175;
		
		cam.update();
		
		
		rot = new Matrix4();
		
		//Set Shader Settings that don't change
		hexWaterShader.begin();
		hexWaterShader.setUniformf("scale", wScale[0], wScale[1], wScale[2]);
		hexWaterShader.setUniformf("color", wColor[0], wColor[1], wColor[2], wColor[3]);
		hexWaterShader.setUniformf("waveSpeed", wWaveSpeed);
		hexWaterShader.setUniformf("noiseSpeed", wNoiseSpeed);
		hexWaterShader.setUniformf("fadeBias", wFadeBias);
		hexWaterShader.setUniformf("fadeExp", wFadeExp);
		hexWaterShader.end();
		
		toonShader.begin();
		toonShader.setUniformf("light0", light0[0], light0[1], light0[2], light0[3]);
		toonShader.setUniformf("light1", light1[0], light1[1], light1[2], light1[3]);
		toonShader.setUniformf("light1", light2[0], light2[1], light2[2], light0[2]);
		toonShader.end();
		
		waterShader.begin();
		waterShader.setUniformf("color", wColor[0]*3f, wColor[1]*3f, wColor[2]*3f, wColor[3]*3f);
		waterShader.setUniformf("waveSpeed", wWaveSpeed);
		waterShader.setUniformf("noiseSpeed", wNoiseSpeed);
		waterShader.setUniformf("fadeBias", wFadeBias);
		waterShader.setUniformf("fadeExp", wFadeExp);
		waterShader.end();
		Gdx.input.setInputProcessor(input);
		
	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub
		
	}

	
	//should have some improvements
	private void renderBoard() {
		float[] color = black;
		GL20 gl = Gdx.gl20;
		gl.glDisable(GL20.GL_DEPTH_TEST);
		//render empty
		hexEmptyShader.begin(); 
		hexEmptyShader.setUniformMatrix("pMatrix", cam.combined);
		hexEmptyShader.setUniformf("colorMod", color[0],color[1],color[2],color[3] );
		for (int i=0;i<emptyTerrain.size();i++) 
			emptyTerrain.get(i).getModel().render(hexEmptyShader, GL20.GL_TRIANGLES);						
		hexEmptyShader.end(); 	
		
		gl.glActiveTexture(GL20.GL_TEXTURE1);
		grass.bind();
		
		//render grass
		hexTextureShader.begin();
		hexTextureShader.setUniformMatrix("pMatrix", cam.combined);
		hexTextureShader.setUniformf("colorMod", color[0],color[1],color[2],color[3] );
		hexTextureShader.setUniformi("texture", 1);
		for (int i=0;i<grassTerrain.size();i++)
			grassTerrain.get(i).getModel().render(hexTextureShader, GL20.GL_TRIANGLES);
		hexTextureShader.end();
		
		gl.glActiveTexture(GL20.GL_TEXTURE1);
		gl.glBindTexture(GL20.GL_TEXTURE_CUBE_MAP, mTex.get(1));
		
		gl.glActiveTexture(GL20.GL_TEXTURE2);
		noise.bind();
		
		
		
		//render water
		hexWaterShader.begin();
		hexWaterShader.setUniformMatrix("pMatrix", cam.combined);
		hexWaterShader.setUniformf("vp", cam.position.x, cam.position.y, cam.position.z);
		hexWaterShader.setUniformf("time", time);
		hexWaterShader.setUniformf("colorMod", color[0],color[1],color[2],color[3] );
		hexWaterShader.setUniformi("envMap", 1);
		hexWaterShader.setUniformi("noise", 2);
		for (int i=0;i<waterTerrain.size();i++)
			waterTerrain.get(i).getModel().render(hexWaterShader, GL20.GL_TRIANGLES);
		
		
		hexWaterShader.end();
		
		
		
		Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST);
		
		//render freshWater resources
		waterShader.begin();
		waterShader.setUniformMatrix("pMatrix", cam.combined);
		waterShader.setUniformf("vp", cam.position.x, cam.position.y, cam.position.z);
		waterShader.setUniformf("time", time);
		waterShader.setUniformi("envMap", 1);
		waterShader.setUniformi("noise", 2);
		for (int i=0;i<waterResources.size();i++) {
			VisResource vr = waterResources.get(i);
			waterShader.setUniformMatrix("mvMatrix", vr.getMat4());
			vr.getModel().render(waterShader, GL20.GL_TRIANGLES);
		}
		waterShader.end();
		
		
		Gdx.gl20.glActiveTexture(GL20.GL_TEXTURE1);
		toon.bind();
		
		toonShader.begin();
			toonShader.setUniformMatrix("pMatrix", cam.combined);
			toonShader.setUniformf("vp", cam.position.x, cam.position.y, cam.position.z, 1.0f);
			toonShader.setUniformi("outline", 1);
		
			toonShader.setUniformf("material", .2f, .1f, .1f, 0.5f);
			//render mountains
			for (int i=0;i<mountainTerrain.size();i++) {
				toonShader.setUniformMatrix("mvMatrix", mountainTerrain.get(i).getValue().getMat4());
				mountain.render(toonShader, GL20.GL_TRIANGLES);
			}
			
			//render resources
			toonShader.setUniformf("material", .4f, .4f, .4f, 0.5f);
			for (int i=0;i<resources.size();i++) {
				VisResource vr = resources.get(i);
				toonShader.setUniformMatrix("mvMatrix", vr.getMat4());
				vr.getModel().render(toonShader, GL20.GL_TRIANGLES);
			}
		toonShader.end();
		
		
		
		
		
	}
	

	
	
	private void renderPlayers() {
		
		//Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST);
		
		Gdx.gl20.glActiveTexture(GL20.GL_TEXTURE1);
		toon.bind();
	
		toonShader.begin();
			toonShader.setUniformMatrix("pMatrix", cam.combined);
			toonShader.setUniformf("vp", cam.position.x, cam.position.y, cam.position.z, 1.0f);
			toonShader.setUniformi("outline", 1);
			for (int i=0;i<players.size();i++) {
				VisPlayer vplayer = players.get(i);
				float[] color = vplayer.getPlayer().getColor();
				toonShader.setUniformMatrix("mvMatrix", vplayer.getMat4());
				toonShader.setUniformf("material", color[0],color[1],color[2], 1.0f);
				vplayer.getModel().render(toonShader, GL20.GL_TRIANGLES);
			}
			
			
		toonShader.end();
	
		//Gdx.gl20.glDisable(GL20.GL_DEPTH_TEST);
	}
	
	private void checkMouseIntersection() {
		

		//don't do any selections if camera is panning
		//if (!input.rightMouseDown)
		{
		
		//give GUI priority for mouse clicks
		int x = Gdx.input.getX();
		int y = Gdx.input.getY();
		Vector2 out = new Vector2();
		stage.toStageCoordinates(x, y, out);
		if (stage.hit(out.x, out.y) != null) {
			return;
		}
			
		GL20 gl = Gdx.gl20;
		
		//find the hex-sq that the mouse is over and rerender that location
		Ray r = cam.getPickRay(x, y);
		
		//first check if any models are moused over
		for (int i=0;i<moverModels.size();i++) {
			VisModel vm = moverModels.get(i);
			Point p;
			if (Intersector.intersectRayBoundsFast(r, vm.getBox()) && vm != myPlayer) {
				moverModel = vm;
				if (vm instanceof VisPlayer)
					p = ((VisPlayer) vm).getPlayer().getPlayerLocation();
				else //only other option is VisResource
					p = ((VisResource) vm).getResource().getResourceLocation();
				checkMove(visBoard[p.x][p.y].getTerrain());
				return;
			}
			else moverModel = null;
			
		}
		

		//board is located at z=0 so the distance must put z at 0... -origin.z/direction.z
		float distance = -r.origin.z / r.direction.z;
		Vector3 end = r.getEndPoint(distance);
				
		//get hex squares around found point to determine location of mouse
		//translate into x=-0.2 to x=0.2 and y=-0.2 to 0.2
				
		int modx = (int) Math.floor(visBoard.length / 2f);
		int mx =  -(int) Math.ceil(end.y/0.32 -0.4);//+ (int)Math.floor(visBoard.length/2);
		if (mx ==0  ||( mx >= -modx && mx < modx)) {
			
			int fx = mx + modx;
			
			float mod;
			if (modx > 0) mod = -1f;
			else mod = 1f;
			
			int fy =(int) Math.ceil((end.x-(mod*mx%2f)*0.2f)/0.41f-0.4f)+ (int) Math.floor(visBoard[fx].length / 2f) ;
					
			//possible terrains at mouse location surround the location fx fy
			//x-1, x+1, y-1, y+1, x-1 y+1, x-1 y-1, x+1 y+1, x+1 y-1
					
			VisLocation mOver = null;
			
			//System.out.println( (fx) + " " + (fy ));
			for (int i=-1;i<2 && mOver == null;i++) {
				for (int j=-1;j<2;j++) {
					VisLocation testLoc;
					int a,b;
					if ( (a=fx+i) < visBoard.length && a >= 0 && (b=fy+j) < visBoard[a].length && b >= 0 && (testLoc = visBoard[a][b]).getTerrain().getModel() != null)
					if (Intersector.intersectRayTriangles(r, testLoc.getTerrain().getVertices(), testLoc.getTerrain().getIndices(), 3, null))
					{
						mOver = testLoc;
						break;
					}

				}
			}
			if (mOver != null) {
				//redraw terrain
				//TODO: check against available moves
				checkMove(mOver.getTerrain());
				
			}

		}
		

		}
			
	}
	
	private void checkMove(VisTerrain sel) {
		GL20 gl = Gdx.gl20;
		
		float[] color = red;
		//color based on if move is valid
		boolean valid = false;
		for (int i=0;i<moves.size();i++) {
			if (moves.get(i).equals(sel.getTerrain().getTerrainLocation()))  {
				color = green;
				valid = true;
			}
		}
		
		switch(sel.getTerrain().getTerrainType()) {
		case Terrain.EMPTY: //render empty
						hexEmptyShader.begin(); 
						hexEmptyShader.setUniformMatrix("pMatrix", cam.combined);
						hexEmptyShader.setUniformf("colorMod", color[0],color[1],color[2],color[3] );
						sel.getModel().render(hexEmptyShader, GL20.GL_TRIANGLES);						
						hexEmptyShader.end();
						break;
		case Terrain.WATER: //render water
						hexWaterShader.begin();
						gl.glActiveTexture(GL20.GL_TEXTURE1);
						gl.glBindTexture(GL20.GL_TEXTURE_CUBE_MAP, mTex.get(1));
						gl.glActiveTexture(GL20.GL_TEXTURE2);
						noise.bind();
						hexWaterShader.setUniformMatrix("pMatrix", cam.combined);
						hexWaterShader.setUniformf("vp", cam.position.x, cam.position.y, cam.position.z);
						hexWaterShader.setUniformf("time", time);
						hexWaterShader.setUniformf("colorMod", color[0],color[1],color[2],color[3] );
						hexWaterShader.setUniformi("envMap", 1);
						hexWaterShader.setUniformi("noise", 2);
						sel.getModel().render(hexWaterShader, GL20.GL_TRIANGLES);
						hexWaterShader.end();break;
		case Terrain.GRASS: //render grass
						
						hexTextureShader.begin();
						gl.glActiveTexture(GL20.GL_TEXTURE1);
						grass.bind();
						hexTextureShader.setUniformMatrix("pMatrix", cam.combined);
						hexTextureShader.setUniformf("colorMod", color[0],color[1],color[2],color[3] );
						hexTextureShader.setUniformi("texture", 1);
						sel.getModel().render(hexTextureShader, GL20.GL_TRIANGLES);
						hexTextureShader.end();
						break;
		case Terrain.MOUNTAIN: break;
		}
		
		if (valid && input.leftMouseDown && Gdx.input.justTouched())
		visualizationStarter.moved(sel.getTerrain().getTerrainLocation());
	}
	
	
	private void renderGUI() {
		
		boolean isMPressed = input.rightMouseDown;
		boolean isLPressed = input.leftMouseDown;
	
		if (moverModel != null && moverModel != myPlayer && moverModel != selectedModel) {
			Matrix4 mvMatrix = moverModel.getMat4();
			Vector3 position = new Vector3(mvMatrix.val[Matrix4.M03],mvMatrix.val[Matrix4.M13], mvMatrix.val[Matrix4.M23]);
			cam.project(position);
			
			mover_container.x = position.x+30;
			mover_container.y = position.y+30;
			mover_container.visible = true;
			if (input.rightJustUp) {
				selectedModel = moverModel;
			}
			if (moverModel instanceof VisPlayer) {
				lbl_moverpre.setText(prestats);
				lbl_moverStats.setText(moverModel.getMoverText());
			}
			else {
				lbl_moverpre.setText(moverModel.getMoverText());
				lbl_moverStats.setText("");
			}
		}
		else if ( !isLPressed) {
			mover_container.visible = false;
			if (input.rightJustUp) {
				select_container.visible = !select_container.visible;
				if (!select_container.visible) {
					selectedModel = null;
				}
			}
		}
		
		chk_autofollow.setChecked(autofollow);
		
		if (selectedModel != null) {
			Matrix4 mvMatrix = selectedModel.getMat4();
			Vector3 position = new Vector3(mvMatrix.val[Matrix4.M03],mvMatrix.val[Matrix4.M13], mvMatrix.val[Matrix4.M23]);
			cam.project(position);
			
			select_container.x = position.x+30;
			select_container.y = position.y+30;
			if (position.z > 1) select_container.visible = false;
			else select_container.visible = true;
			
			//Creature c = ((VisPlayer)selectedModel).getPlayer().getCreature();
			
			
			//mover_stats.setText("test");
			if (selectedModel instanceof VisPlayer) {
				lbl_selectpre.setText(prestats);
				lbl_selectStats.setText(selectedModel.getMoverText());
			}
			else {
				lbl_selectpre.setText(selectedModel.getMoverText());
				lbl_selectStats.setText("");
			}
			
		}
		else if (select_container.visible) select_container.visible = false;
		
		
		//render myPlayer stats
		if (myPlayer != null) {
		Creature c = myPlayer.getPlayer().getCreature();
		lbl_myStats.setText( c.getBaseStat(Creature.STAT_HEALTH) + "  /  " + c.getBaseStat(Creature.STAT_MAX_HEALTH) + "\n" + 
		c.getBaseStat(Creature.STAT_STRENGTH) + "\n" +
		c.getBaseStat(Creature.STAT_ARMOR) + "\n" + 
		c.getBaseStat(Creature.STAT_SIGHT) + "\n" +
		c.getBaseStat(Creature.STAT_SPEED) + "\n");
		
		Collection<Buff> buffs = c.getBuffs();
		Iterator<Buff> i = buffs.iterator();
		float speed =0, armor=0, sight=0, strength=0;
		for (;i.hasNext();) {
			Buff b = i.next();
			switch (b.getStat()) {
			case Creature.STAT_SPEED : speed += b.getValue(); break;
			case Creature.STAT_ARMOR : armor += b.getValue(); break;
			case Creature.STAT_SIGHT : sight += b.getValue(); break;
			case Creature.STAT_STRENGTH : strength += b.getValue(); break;
			}
		}
		
		lbl_myBuffs.setText("\n+" + strength + "\n+" + armor + "\n+" + sight + "\n+" + speed);
		}
		Gdx.gl20.glActiveTexture(GL20.GL_TEXTURE0);
		stage.draw();
	}
	

	private void testKeyPress(float dt) {
		if (Gdx.input.isKeyPressed(Keys.UP)) {
			cam.position.y += dt * cam.up.y;
			cam.position.x += dt * cam.up.x;
			setTargetDistance();
			//autofollow = false;
		}
		else if (Gdx.input.isKeyPressed(Keys.DOWN)) {
			cam.position.y -= dt * cam.up.y;
			cam.position.x -= dt * cam.up.x;
			setTargetDistance();
			//autofollow = false;
		}
		
		if (Gdx.input.isKeyPressed(Keys.LEFT)) {
			//cam.position.x -= cam.direction.x * dt;
			cam.position.y += dt * cam.up.x;
			cam.position.x -= dt * cam.up.y;
			setTargetDistance();
			//autofollow = false;
		}
		else if (Gdx.input.isKeyPressed(Keys.RIGHT)) {
			cam.position.y -= dt * cam.up.x;
			cam.position.x += dt * cam.up.y;
			setTargetDistance();
			//autofollow = false;
		}
		//setTargetDistance();
	}
	
	private void setTargetDistance() {
		if (!autofollow) return;
		float[] mvMatrix = myPlayer.getMat4().val;
		targetDistance.x = mvMatrix[Matrix4.M03] - cam.position.x;
		targetDistance.y = mvMatrix[Matrix4.M13] - cam.position.y;
	}
	
	public void autoMoveCamera() {

		float[] mvMatrix = myPlayer.getMat4().val;
		cam.position.x =   mvMatrix[Matrix4.M03] - targetDistance.x;
		cam.position.y =   mvMatrix[Matrix4.M13] - targetDistance.y;
		cam.lookAt(mvMatrix[Matrix4.M03],mvMatrix[Matrix4.M13],0);//cam.position.x, cam.position.y, 0);
		
		
	}
	
	@Override
	public void render() {
		
		
		GL20 gl = Gdx.gl20;
		gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
		
		float dTime = Gdx.graphics.getDeltaTime();
		testKeyPress(dTime);
		
		//setTargetDistance();
		if (autofollow) 
			autoMoveCamera();
		cam.up.z = 1;
		cam.normalizeUp();
		cam.update();
		
		time +=   0.1 * dTime;
		if (time > 6.28f) 
			time = 0;
		
		
		
		
		//renderWorld();
		renderBoard();
		renderPlayers();
		if (!dialogOpen) checkMouseIntersection();
		renderGUI();
		
		
		if (vn++>60) {vn=0;
		System.out.println(Gdx.graphics.getFramesPerSecond());
		}
		
		if (input.rightJustUp ) input.rightJustUp = false;
		
	}
	int vn=0;

	@Override
	public void resize(int width, int height) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub
		
	}
	

	

	@Override
	public void dispose() {
		for (int i=0;i<visBoard.length;i++) {
			for (int j=0;j<visBoard.length;j++) {
				visBoard[i][j].getTerrain().getModel().dispose();
			}
		}

		world.dispose();
		mountain.dispose();
		gorilla.dispose();
		wolf.dispose();
		bear.dispose();
		elephant.dispose();
		snake.dispose();
		hawk.dispose();
		rabbit.dispose();
		ram.dispose();
		turtle.dispose();
		water.dispose();

		stage.dispose();
		font_small.dispose();
		font_large.dispose();
		
		worldShader.dispose();
		hexEmptyShader.dispose();
		hexWaterShader.dispose();
		hexTextureShader.dispose();
		toonShader.dispose();
		
		
		Gdx.gl20.glDeleteTextures(mTex.capacity(), mTex);
		for (int i=0;i< worldFaces.length;i++) 
			worldFaces[i].dispose();
		noise.dispose();
		grass.dispose();
		toon.dispose();
		blankAlpha.dispose();
		blank.dispose();
		alpha.dispose();
		exitBtn.dispose();
		exitinvBtn.dispose();
		endturnBtn.dispose();
		endturninvBtn.dispose();
		closeBtn.dispose();
		closeinvBtn.dispose();
		checked.dispose();
		unchecked.dispose();
	
	}
	
	protected class inputHandler implements InputProcessor {

		private int lastX, lastY;
		private int rotX, rotY;
		private Boolean rightMouseDown;
		private Boolean rightJustUp, rightDragged;
		private Boolean leftMouseDown;
		
		protected inputHandler() {
			lastX = 0;
			lastY = 0;
			rightMouseDown = false;
			leftMouseDown = false;
			rightJustUp = false;
			rightDragged = false;
		}
		
		

		@Override
		public boolean touchDown(int x, int y, int pointer, int button) {
			if (dialogOpen) {
				Vector2 p = new Vector2();
				stage.toStageCoordinates(x, y, p);
				message_container.toLocalCoordinates(p);
				message_container.touchDown(p.x, p.y,pointer);
			}
			else stage.touchDown(x, y, pointer, button);
			
			if (button == Buttons.RIGHT) 
					rightMouseDown = true;	
			else if (button == Buttons.LEFT)
					leftMouseDown = true;
			lastX = x;
			lastY = y;
			
			return false;
		}
		float xrot=0, yrot=0;
		
		@Override
		public boolean touchDragged(int x, int y, int pointer) {
			//stage.touchDragged(x, y, pointer);
			if (rightMouseDown ) {
				float dt = Gdx.graphics.getDeltaTime() * 50f;
		
				if (autofollow) {
				xrot = dt * (-x+lastX);
				yrot = dt * (-y+lastY)*2f;
				
				
				float cosx = MathUtils.cosDeg(xrot);
				float sinx = MathUtils.sinDeg(xrot);
				float siny = MathUtils.sinDeg(yrot);
				float cosy = MathUtils.cosDeg(yrot);
				
				
				Vector3 v = new Vector3(cam.position.x, cam.position.y, cam.position.z);
				float[] mvMatrix = myPlayer.getMat4().val;
				Vector3 o = new Vector3(mvMatrix[Matrix4.M03], mvMatrix[Matrix4.M13], mvMatrix[Matrix4.M23]);
				
				v.x = cam.position.x = cosx * (v.x - o.x) - sinx * (v.y-o.y) + o.x;
				v.y = cam.position.y = sinx * (v.x - o.x) + cosx * (v.y-o.y) + o.y;
				Vector3 dir = new Vector3(v.x - o.x, v.y - o.y, cam.position.z);
				
				dir.nor();//new Vector3(cam.direction);  //inverse direction
			
				dir.x *= MathUtils.cosDeg(yrot)+MathUtils.sinDeg(yrot) ;
				dir.y *= MathUtils.sinDeg(yrot)+MathUtils.cosDeg(yrot) ;
	
				dir.nor().mul(v.dst(o));
				cam.position.set(dir.add(o));
				
				}
				else {
			
					cam.rotate( -(x-lastX) *dt, 0, 0, Math.abs(cam.up.y) + Math.abs(cam.up.x));
					cam.rotate( -(y-lastY) *dt,- (cam.direction.y*Math.abs(cam.up.y)), (cam.direction.x*Math.abs(cam.up.x)) , 0);
				}
				
				setTargetDistance();
				rightDragged = true;
				
			}
			
			lastX = x;
			lastY = y;
			return false;
		}
		
		@Override
		public boolean scrolled(int amount) {
			float s = amount * 100 * Gdx.graphics.getDeltaTime();
			Vector3 pos = new Vector3(cam.position);
			Vector3 dir = new Vector3(cam.direction);
			
			pos.sub(dir.mul(s));

			if (pos.z > 1.0f){
				cam.position.set(pos);
				setTargetDistance();

			}
			return false;
		}
		
		@Override
		public boolean touchUp(int x, int y, int pointer, int button) {
			if (dialogOpen) {
				Vector2 p = new Vector2();
				stage.toStageCoordinates(x, y, p);
				message_container.toLocalCoordinates(p);
				message_container.touchUp(p.x, p.y,pointer);
			}
			else stage.touchUp(x, y, pointer, button);
			
			if ( button == Buttons.RIGHT && rightMouseDown) {
				rightMouseDown = false;
				if (!rightDragged) rightJustUp = true;

				rightDragged = false;
			}
			else if (button == Buttons.LEFT && leftMouseDown) 
				leftMouseDown = false;
			return false;
		}

		@Override
		public boolean touchMoved(int x, int y) {
			//stage.touchMoved(x, y);
			return false;
		}

		
		@Override
		public boolean keyDown(int keycode) {
			// TODO Auto-generated method stub
			
			return false;
		}

		@Override
		public boolean keyUp(int keycode) {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public boolean keyTyped(char character) {
			
			// TODO Auto-generated method stub
			return false;
		}
		
	}

	
}